from util import *
from MicroService import MicroService
import numpy as np
import copy

class BareMetalNode:
    def __init__(self,cloud_system,node_id):
        #"edge_"+i+".node_"+j
        self.node_id=node_id
        self.cloud_system=cloud_system
        #simpy环境
        self.env=cloud_system.env
        #CPU剩余量
        self.cpu_rest=100
        #memory剩余量
        self.mem_rest=100
        #disk剩余量
        self.disk_rest=100
        
        #记录已经关掉的微服务的资源情况 cpu,mem
        self.used_resource_cpu=0 
        self.used_resource_mem = 0
        
        #记录在当前Node上每种微服务对应的msid
        self.msk_to_msidlist={}
        self.msid_to_msobj={}
        self.ms_order=0 #用于区分不同的ms实例
        #累计成本
        self.acc_cost=0
        #累计资源消耗
        self.acc_cpu=0
        self.acc_mem=0
        self.acc_disk=0
        
        
    #部署微服务
    def deploy_ms(self, ms_kind, without_cold_start=False):
        cpu_need=self.cloud_system.ms_kind_to_resource_need[ms_kind]["cpu"]
        mem_need=self.cloud_system.ms_kind_to_resource_need[ms_kind]["mem"]
        disk_need=self.cloud_system.ms_kind_to_resource_need[ms_kind]["disk"]
        if cpu_need<=self.cpu_rest and mem_need<=self.mem_rest and disk_need<=self.disk_rest:
            ms_id=f"{ms_kind}-{self.node_id}-{self.ms_order}"
            self.ms_order+=1
            ms_new=MicroService(self.cloud_system, self, ms_kind, ms_id, without_cold_start)
            if ms_kind in self.msk_to_msidlist.keys():
                self.msk_to_msidlist[ms_kind].append(ms_id)
            else:
                self.msk_to_msidlist[ms_kind]=[ms_id]
            self.msid_to_msobj[ms_id]=ms_new
            
            self.cpu_rest-=cpu_need
            self.mem_rest-=mem_need
            self.disk_rest-=disk_need
            self.cloud_system.record_ms_start_relavent_time(ms_new)
            return True, ms_new
        else:
            return False, None
    
    #删除微服务
    def remove_ms(self, ms_id):
        aim_ms=self.msid_to_msobj[ms_id]
        if aim_ms.can_be_rm():
            ms_obj=self.msid_to_msobj[ms_id]
            ms_kind=ms_obj.ms_kind
            
            #记录微服务资源消耗
            # self.acc_cost+=ms_obj.get_acc_cost()
            self.acc_cost+=ms_obj.get_cost_by_deploy()
            cpu_usage, mem_usage, disk_usage=ms_obj.get_current_resource_usage()
            self.acc_cpu+=cpu_usage
            self.acc_mem+=mem_usage
            self.acc_disk+=disk_usage
            
            del self.msid_to_msobj[ms_id]
            self.msk_to_msidlist[ms_kind].remove(ms_id)
            #发出信号，给自身，目的：终止状态转换
            ms_obj.remove()
            self.cloud_system.record_ms_end_relavent_time(ms_obj)
            
            self.cpu_rest+=self.cloud_system.ms_kind_to_resource_need[ms_kind]["cpu"]
            self.mem_rest+=self.cloud_system.ms_kind_to_resource_need[ms_kind]["mem"]
            self.disk_rest+=self.cloud_system.ms_kind_to_resource_need[ms_kind]["disk"]
            
            return True
        else:
            aim_ms.prepare_to_rm()
            return False
    
    #获取指定类型的微服务的空闲数量
    def get_idle_num_with_ms_kind(self, ms_kind):
        idle_num=0
        if ms_kind in self.msk_to_msidlist.keys():
            for ms_id in self.msk_to_msidlist[ms_kind]:
                if self.msid_to_msobj[ms_id].is_idle():
                    idle_num+=1
        return idle_num
    
    
    def get_idle_num(self):
        idle_num=0
        for ms_kind in self.msk_to_msidlist.keys():
            for ms_id in self.msk_to_msidlist[ms_kind]:
                if self.msid_to_msobj[ms_id].is_idle():
                    idle_num+=1
        return idle_num
    
    
    #获取指定类型的微服务对象
    def get_available_ms_obj_with_ms_kind(self, ms_kind):
        ms_obj_list=[]
        if ms_kind in self.msk_to_msidlist.keys():
            for ms_id in self.msk_to_msidlist[ms_kind]:
                if self.msid_to_msobj[ms_id].is_available_for_task():
                    ms_obj_list.append(self.msid_to_msobj[ms_id])
        return ms_obj_list
    
    def judge_node_idle(self):
        for ms in self.msid_to_msobj.values():
            if not ms.judge_ms_idle():
                return False
        return True
    
    def get_acc_cost(self):
        exist_cost=0
        for ms_obj in self.msid_to_msobj.values():
            exist_cost+=ms_obj.get_cost_by_deploy()
        return self.acc_cost+exist_cost

    def rm_all_ms(self):
        ms_id_list=[]
        for ms_id in self.msid_to_msobj.keys():
            ms_id_list.append(ms_id)
        for ms_id in ms_id_list:
            self.remove_ms(ms_id)
            
            
    def get_acc_resource_usage(self):
        cur_cpu=0
        cur_mem=0
        cur_disk=0
        for ms_obj in self.msid_to_msobj.values():
            cpu_usage, mem_usage, disk_usage=ms_obj.get_current_resource_usage()
            cur_cpu+=cpu_usage
            cur_mem+=mem_usage
            cur_disk+=disk_usage
        return self.acc_cpu+cur_cpu, self.acc_mem+cur_mem, self.acc_disk+cur_disk
    
    ################################################################################################
   
    
    def record_ms_id_for_deploy(self):
        self.ms_info_for_restart={}
        for ms_name in self.ms_to_msidlist.keys():
            self.ms_info_for_restart[ms_name]=len(self.ms_to_msidlist[ms_name])
        return

    def auto_ms_deploy(self):
        yield self.env.timeout(self.next_time_point-self.env.now)
        for ms_name in self.ms_info_for_restart.keys():
            self.deploy_ms(ms_name,self.ms_info_for_restart[ms_name])
        self.ms_info_for_restart=None

    def _set_state(self, new_state, new_time_point):
        if self.cloud_edge_system.print_flage == True:
            print("********time:", '%.6f'%self.env.now, "\t", self.node_id, "\t incoming state:\t",new_state, "\tnext_time:", '%.6f'%new_time_point)
        if new_state == "transient failure":
            self.next_time_point = new_time_point
            return

        elif new_state == "persistent failure" or new_state == "normal":
            self.state = new_state
            self.next_time_point = new_time_point
            return
        else:
            print("node _set_state() wrong")
            exit(-1)




    #删除微服务 模式分为被动删除，即无法重调度任务。和主动删除，可重调度任务(active, passive)
    def remove_ms_by_id(self,ms_id,mode):
        self.record_ms_resource_cost(ms_id)
        if mode == "passive": #平台被动移除，即用户进行操作
            # 处理微服务移除后的一些工作，比如将任务队列中的任务重调度到其他微服务上
            self.ms_obj[ms_id].task_in_queue_rescheduling()
        elif mode != "active":#平台主动移除，即平台自行删除
            print("ms remove mode wrong!")
            exit(-1)

        # todelet_ms=self.ms_obj[ms_id]
        del self.ms_obj[ms_id]

        #移除本服务器上记录的相关信息
        ms_name=ms_id.split(".")[2].split(":")[0]
        self.ms_to_msidlist[ms_name].remove(ms_id)
        if self.cloud_edge_system.print_flage == True:
            print("********ACTION(orchestra)time:", self.env.now, "\tmode:", mode,"\t",ms_id, "\t remove from \t", self.node_id)
        return

    # 删除微服务
    def remove_ms_by_name(self, ms_name):

        #需要评估删除哪一个 （队列最短的微服务，尽量是当前没有任务的）
        ms_id=self._get_less_occupy_ms_id(ms_name)
        if ms_id == None:
            return None
        else:
            # if self.cloud_edge_system.print_flage == True:
            #     print("time:", self.env.now, "\t", ms_id, "\t remove from \t", self.node_id)
            self.remove_ms_by_id(ms_id,)
        return ms_name

    def record_ms_resource_cost(self,ms_id):
        used_cpu, used_mem = self.ms_obj[ms_id].get_used_resource_quantity()
        self.used_resource_cpu += used_cpu
        self.used_resource_mem += used_mem
        return

    # 寻找最少占用的微服务ID
    def select_aim_ms_for_task_record(self,ms_name):
        return self._get_less_occupy_ms_id(ms_name)


    def task_record_arrive(self,task_record_f,ms_id):
        if self.judge_exist_ms_id(ms_id):
            #将任务放入对应微服务的队列中
            self.ms_obj[ms_id].add_task_record_to_queue(task_record_f)
        return


    def _get_less_occupy_ms_id(self,ms_name):
        aim_ms_id = ""
        score = 9999
        if ms_name not in self.ms_to_msidlist.keys() or len(self.ms_to_msidlist[ms_name])==0:
            return None
        for ms_id in self.ms_to_msidlist[ms_name]:
            if self.ms_obj[ms_id].get_resource_occupy_score()< score:
                aim_ms_id = ms_id
        if aim_ms_id == "":
            print(aim_ms_id)
        return aim_ms_id


    #返回当前资源使用量，包括CPU和mem
    def get_used_resource_quantity(self):
        temp_used_resource_cpu=self.used_resource_cpu
        temp_used_resource_mem=self.used_resource_mem
        for ms_id in self.ms_obj.keys():
            used_cpu,used_mem=self.ms_obj[ms_id].get_used_resource_quantity()
            # if self.node_id == "edge_1.node_0":
            #     print(ms_id,"\t",used_cpu,"\t",used_mem)
            temp_used_resource_cpu+=used_cpu
            temp_used_resource_mem+=used_mem
        # print(self.node_id,"\t",temp_used_resource_cpu,"\t",temp_used_resource_mem)
        return temp_used_resource_cpu, temp_used_resource_mem

    

    def judge_exist_normal_ms(self,ms_name):
        if ms_name in self.ms_to_msidlist.keys() and len(self.ms_to_msidlist[ms_name])>0:
            for ms_id in self.ms_to_msidlist[ms_name]:
                if self.ms_obj[ms_id].is_normal():
                    return True
        else:
            return False

    def judge_exist_ms_id(self,ms_id):
        if ms_id in self.ms_obj.keys() and self.ms_obj[ms_id].state == "normal":
            return True
        else:
            return False

    def get_aim_ms_ave_queue_len(self,ms_name):
        if not self.judge_exist_normal_ms(ms_name):
            return None
        queue_len_list=[]
        for ms_id in self.ms_to_msidlist[ms_name]:
            queue_len_list.append(self.ms_obj[ms_id].get_queue_len())

        return np.mean(queue_len_list)

    def get_cross_node_delay(self):
        #to do
        return 1